// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/media/android/browser_surface_view_manager.h"

#include "base/android/build_info.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/media/surface_view_manager_messages_android.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_delegate.h"
#include "gpu/ipc/common/gpu_surface_tracker.h"
#include "media/base/surface_manager.h"
#include "ui/gfx/geometry/size.h"

namespace content {
namespace {
    void SendDestroyingVideoSurfaceOnIO(int surface_id,
        const base::Closure& done_cb)
    {
        GpuProcessHost* host = GpuProcessHost::Get(
            GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false /* force_create */);
        if (host)
            host->SendDestroyingVideoSurface(surface_id, done_cb);
        else
            done_cb.Run();
    }
} // namespace

BrowserSurfaceViewManager::BrowserSurfaceViewManager(
    RenderFrameHost* render_frame_host)
    : render_frame_host_(render_frame_host)
    , surface_id_(media::SurfaceManager::kNoSurfaceID)
{
}

BrowserSurfaceViewManager::~BrowserSurfaceViewManager() { }

void BrowserSurfaceViewManager::SetVideoSurface(gl::ScopedJavaSurface surface)
{
    TRACE_EVENT0("media", "BrowserSurfaceViewManager::SetVideoSurface");
    if (surface.IsEmpty()) {
        DCHECK_NE(surface_id_, media::SurfaceManager::kNoSurfaceID);
        gpu::GpuSurfaceTracker::Get()->RemoveSurface(surface_id_);
        gpu::GpuSurfaceTracker::Get()->UnregisterViewSurface(surface_id_);
        SendDestroyingVideoSurface(surface_id_);
        surface_id_ = media::SurfaceManager::kNoSurfaceID;
    } else {
        // We just use the surface tracker to allocate a surface id for us. The
        // lookup will go through the Android specific path and get the java
        // surface directly, so there's no need to add a valid native widget here.
        surface_id_ = gpu::GpuSurfaceTracker::Get()->AddSurfaceForNativeWidget(
            gfx::kNullAcceleratedWidget);
        gpu::GpuSurfaceTracker::GetInstance()->RegisterViewSurface(
            surface_id_, surface.j_surface().obj());
        SendSurfaceID(surface_id_);
    }
}

void BrowserSurfaceViewManager::DidExitFullscreen(bool release_media_player)
{
    DVLOG(3) << __func__;
    content_video_view_.reset();
}

void BrowserSurfaceViewManager::OnCreateFullscreenSurface(
    const gfx::Size& video_natural_size)
{
    // If we are in virtual reality, no surface view is needed so just return.
    // TODO(http://crbug.com/673886): Support overlay surfaces in VR using GVR
    // reprojection video surface.
    RenderWidgetHostViewBase* rwhvb = static_cast<RenderWidgetHostViewBase*>(render_frame_host_->GetView());
    if (rwhvb->IsInVR()) {
        SendSurfaceID(media::SurfaceManager::kNoSurfaceID);
        return;
    }

    // It's valid to get this call if we already own the fullscreen view. We just
    // return the existing surface id.
    if (content_video_view_) {
        // Send the surface now if we have it. Otherwise it will be returned by
        // |SetVideoSurface|.
        if (surface_id_ != media::SurfaceManager::kNoSurfaceID) {
            SendSurfaceID(surface_id_);
            OnNaturalSizeChanged(video_natural_size);
            return;
        }
    }

    // If we don't own the fullscreen view, but one exists, it means another
    // WebContents has it. Ignore this request and return a null surface id.
    if (ContentVideoView::GetInstance()) {
        SendSurfaceID(media::SurfaceManager::kNoSurfaceID);
        return;
    }

    WebContents* web_contents = WebContents::FromRenderFrameHost(render_frame_host_);
    if (!web_contents->GetDelegate()) {
        SendSurfaceID(media::SurfaceManager::kNoSurfaceID);
        return;
    }
    ContentViewCore* cvc = ContentViewCore::FromWebContents(web_contents);
    content_video_view_.reset(
        new ContentVideoView(this, cvc,
            web_contents->GetDelegate()->GetContentVideoViewEmbedder(),
            video_natural_size));
}

void BrowserSurfaceViewManager::OnNaturalSizeChanged(const gfx::Size& size)
{
    if (content_video_view_)
        content_video_view_->OnVideoSizeChanged(size.width(), size.height());
}

bool BrowserSurfaceViewManager::SendSurfaceID(int surface_id)
{
    return render_frame_host_->Send(
        new SurfaceViewManagerMsg_FullscreenSurfaceCreated(
            render_frame_host_->GetRoutingID(), surface_id));
}

void BrowserSurfaceViewManager::SendDestroyingVideoSurface(int surface_id)
{
    base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
        base::WaitableEvent::InitialState::NOT_SIGNALED);
    // Unretained is okay because we're waiting on the callback.
    if (BrowserThread::PostTask(
            BrowserThread::IO, FROM_HERE,
            base::Bind(&SendDestroyingVideoSurfaceOnIO, surface_id,
                base::Bind(&base::WaitableEvent::Signal,
                    base::Unretained(&waiter))))) {
        base::ThreadRestrictions::ScopedAllowWait allow_wait;
        waiter.Wait();
    }
}

} // namespace content
